#ifdef _RMS_TOML_

#include "Network_a.h"

CUDPClient::CUDPClient()
{
  ErrorID = eNoError;
  ErrorCode = 0;
  packets_in=0;

#ifdef _WIN32_64_
// initializing WinSock:
  WORD wVersionRequested;
  WSADATA wsaData;

  wVersionRequested = MAKEWORD( 2, 0 );
  WSAStartup( wVersionRequested, &wsaData );
#endif

  InitializeCriticalSection(&errCS);
  InitializeCriticalSection(&recvCS);
  InitializeCriticalSection(&termCS);
  hDone=CreateEventHandle(NULL, FALSE, FALSE, NULL );
  SetEvent(hDone);// no pending threads
  NotTerminated = true;
}

CUDPClient::~CUDPClient()
{
  Terminate();
  DeleteCriticalSection(&termCS);
  DeleteCriticalSection(&recvCS);
  DeleteCriticalSection(&errCS);
  CloseHandle(hDone);
}

bool CUDPClient::Start(const NetClient& aServ)
{
  int i;
  bool ok = true;
  serv = aServ;
  packets_in=0;

// set port:
  _log(strtime()+"Initializing network...\n");

  sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

  ok = sock!=SOCKET_ERROR;

#ifdef _WIN32_64_
    if(ok)
    {
      u_long nonblocking = 0;
      ioctlsocket(sock,FIONBIO,&nonblocking); // set socket type to blocking
    }
#endif

  if(!ok)
  {
    SetError(eNoAdapters);
    NotTerminated = false;
    ok = false;
  } else ok = true;
  ResetEvent(hDone);
  hThread=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)RecvThread,this,0,0);
  return ok;
}

//void __cdecl CUDPClient::RecvThread(void* lpv)
DWORD WINAPI CUDPClient::RecvThread(void* lpv)
{
  CUDPClient* Self = (CUDPClient*) lpv;

  fd_set rfd;
  timeval timeout = {5,0};

  sockaddr_in cl;
  int clsize = sizeof(cl);
  BYTE buf[MaxBufSize];
  int datalen;


  while(Self->NotTerminated)
  {
//    Sleep(10);
    FD_ZERO(&rfd);
    FD_SET(Self->sock,&rfd);

    int count = select(Self->sock+1,&rfd,NULL,NULL,&timeout);
	SetEvent(SystemTimerEvent);
    if(count==SOCKET_ERROR)
    {
      Self->SetError(eSelect,0);
      goto _exit;
    }

    if(count>0)
    { // read data
#ifdef _WIN32_64_
      datalen = recvfrom(Self->sock, (char*)buf, MaxBufSize, 0, (sockaddr*)&cl, &clsize);
#else
      datalen = recvfrom(Self->sock, (char*)buf, MaxBufSize, 0, (sockaddr*)&cl, (socklen_t*)&clsize);
#endif
      if(!datalen || (datalen==SOCKET_ERROR))
      {
#ifdef _WIN32_64_
		count = WSAGetLastError();
#endif
        continue;
      }
	  Self->packets_in++; 
      NetClient nc(cl);
      EnterCriticalSection(&Self->recvCS);
      Self->Messages.push(CNetworkMessage(nc,buf,datalen));
      LeaveCriticalSection(&Self->recvCS);
    }

    int q = 0;
    while(true) // wait while some messages from queue will be read
    {
      if(Self->MessagesCount()<=MaxMessagesInQueue) break;
//      Sleep(QueueWait); 
      WaitForSingleObject(SystemTimerEvent, INFINITE); q+=QueueWait;
      if(q > QueueTimeOut)
      {
        Self->SetError(eRecvQueueTimeOut,0);
        goto _exit;
      }
    }
  }
_exit:
  SetEvent(Self->hDone);
  Self->hThread=0;
  return 0;
}

int CUDPClient::MessagesCount()
{
  EnterCriticalSection(&recvCS);
  int res = Messages.size();
  LeaveCriticalSection(&recvCS);
  return res;
}

CNetworkMessage CUDPClient::PopMessage()
{
  EnterCriticalSection(&recvCS);
  CNetworkMessage msg = Messages.front();
  Messages.pop();
  LeaveCriticalSection(&recvCS);
  return msg;
}

void CUDPClient::SetError(CErrorID ID, int Code)
{
  EnterCriticalSection(&errCS);
  ErrorID = ID;
  ErrorCode = Code;
  LeaveCriticalSection(&errCS);
  return;
}

CErrorID CUDPClient::GetError()
{
  EnterCriticalSection(&errCS);
  CErrorID res = ErrorID;
  LeaveCriticalSection(&errCS);
  return res;
}

string CUDPClient::GetErrorText()
{
  ostringstream ss;
  string s;

  switch(GetError())
  {
    case eNoError:
    {
      s = "No Error";
      break;
    };
    case eSelect:
    {
      s = "Select Error";
      break;
    };
    case eRecvFrom:
    {
      s = "Receive Error";
      break;
    };
    case eRecvQueueTimeOut:
    {
      s = "Incoming Queue Overflow";
      break;
    };
    case eNoAdapters:
    {
      s = "No available network adapters found";
      break;
    };
    default:
    {
      s = "Unknown Error";
      break;
    }
  }
  ss << "ERROR: " << s;
  return ss.str();
}

int CUDPClient::GetErrorCode()
{
  EnterCriticalSection(&errCS);
  int x = ErrorCode;
  LeaveCriticalSection(&errCS);
  return x;
}

bool CUDPClient::IsNotTerminated()
{
  EnterCriticalSection(&termCS);
  bool res = NotTerminated;
  LeaveCriticalSection(&termCS);
  return res;
}

void CUDPClient::Terminate()
{
  EnterCriticalSection(&termCS);
  NotTerminated = false;
  LeaveCriticalSection(&termCS);
  
  if (sock) {
	#ifdef _WIN32_64_
		closesocket(sock);
		WSACleanup();
	#else
		close(sock);
	#endif
	sock=0;
  }  
  if(hThread) {
 	  WaitForSingleObject(hDone, INFINITE);
	  SetEvent(hDone);
  }
}

bool CUDPClient::SendMsg(const CNetworkMessage* pMessage)
{
//  Sleep(1);
WaitForSingleObject(SystemTimerEvent, INFINITE);
  bool ok = sendto(
    sock,
    (char*) pMessage->Data,
    pMessage->DataLen,
    0,
    (sockaddr*)& pMessage->Client.Addr,
    sizeof(pMessage->Client.Addr)
  ) == pMessage->DataLen;
  if(!ok)
    SetError(eSendError, 0);
  return ok;
}


#endif